home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / GDIMETA.PAK / CLIENT.C < prev    next >
C/C++ Source or Header  |  1997-05-06  |  48KB  |  1,701 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993-1995  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE: client.c
  9. //
  10. //  PURPOSE: Contains all input/drawing routines for the GDI Input sample.
  11. //
  12. //  FUNCTIONS:
  13. //    CreateClientWindow  - Creates the client window that handles input.
  14. //    ClientWndProc       - Processes messages for the client window.
  15. //    CmdDrawMode         - Changes the current drawing mode.
  16. //    CmdFill             - Toggles the fill mode.
  17. //    CmdCreatePen        - Puts up Pen Style dialog box.
  18. //    CmdCreateBrush      - Puts up Brush Style dialog box.
  19. //    MsgClientCreate     - Creates initial pens & brushes.
  20. //    MsgClientDestroy    - Frees owned objects.
  21. //    MsgClientKeyDown    - Looks for Escape key to cancel drawing.
  22. //    MsgClientPaint      - Paints the client window.
  23. //    MsgClientSize       - Updates scrollbars when window is resized
  24. //    MsgClientScroll     - Handles scrolling
  25. //
  26. //    MsgClientLButtonDown
  27. //    MsgClientMouseMove => Pass mouse messages to the current handlers.
  28. //    MsgClientLButtonUp
  29. //
  30. //    PixelLBDown
  31. //    PixelMouseMove    ==> Draw individual pixels with SetPixelV.
  32. //    PixelLBUp
  33. //
  34. //    BezierLBDown
  35. //    BezierMouseMove   ==> Select 4 points for drawing a Bezier curve.
  36. //    BezierLBUp
  37. //
  38. //    RectLBDown
  39. //    RectMouseMove     ==> Select 2 points for a line, rect, or ellipse.
  40. //    RectLBUp
  41. //
  42. //    LineDraw              Given 2 points, draw a line, rect, or ellipse.
  43. //    RectDraw          ==> These functions all take identical parameters
  44. //    EllipseDraw           so they can be used interchangeably.
  45. //
  46. //    StartRubberBand     - Initiates drawing procedure.
  47. //    EndRubberBand       - Ends drawing procedure.
  48. //    ClientNewDrawing    - Called when the current picture changes
  49. //    SetupScrollBars     - Turns scrollbars on/off
  50. //
  51. //  COMMENTS:
  52. //
  53.  
  54. #include <windows.h>            // required for all Windows applications
  55. #include <windowsx.h>
  56. #include <commctrl.h>           // prototypes and defs for common controls
  57. #include "globals.h"            // prototypes specific to this application
  58. #include "resource.h"
  59. #include "toolbar.h"            // prototypes for the tool bar
  60. #include "statbar.h"            // prototypes for the status bar
  61. #include "pendlg.h"             // ID's and prototypes for the Pen dialog
  62. #include "brushdlg.h"           // ID's and prototypes for the Brush dialog
  63. #include "palette.h"            // prototypes for palette message handlers
  64. #include "metafile.h"
  65.  
  66. // Global variables local to this file
  67.  
  68. HPEN    hpenBlack;              // Useful stock objects
  69. HBRUSH  hbrNull;
  70.  
  71. HPEN    hpenDraw;               // for drawing lines and frames
  72. HBRUSH  hbrReal;                // for filling interiors
  73. HBRUSH  hbrDraw;                // equals either hbrReal or hbrNull
  74.  
  75. LOGPEN  logPen;
  76. LOGBRUSH logBrush;
  77.  
  78. HDC     hdcRB;                  // DC for Rubber Banding
  79. BOOL    bDrawing;               // Flag to indicate when we are drawing
  80. UINT    uDrawMode;              // IDM_LINE, IDM_RECT, etc.
  81.  
  82. HDC     hdcBitmap;              // Used with hbmBitmap
  83. HBITMAP hbmBitmap;              // All drawing is cached here for faster screen updates
  84. UINT    cxBitmap;               // Width of bitmap
  85. UINT    cyBitmap;               // Height of bitmap
  86. HBITMAP hbmStock;               // Stock 1x1 bitmap that mem DC's are initialized with
  87.  
  88. HDC     hdcEMF;                 // DC for recording to metafile
  89. UINT    cxPict;                 // Dimensions of current metafile
  90. UINT    cyPict;
  91.  
  92. #define BEZ_MAXPOINTS   4       // # of points in a Bezier curve
  93. POINT   pPoints[BEZ_MAXPOINTS]; // Stores coordinates for various drawing fns
  94. UINT    cPoints;                // Current # of points (for bezier)
  95.  
  96. // for scrolling
  97. int cxHorzScrollPos = 0;
  98. int cyVertScrollPos = 0;
  99.  
  100.  
  101. // The following 3 function pointers are used to point to the current
  102. // mouse handling functons.  Different drawing types can each define a
  103. // set of mouse functions.  When a new drawing mode is selected, these
  104. // pointers are set to point to the correct functions for that mode.
  105. // The main mouse message handlers then dereference these pointers to
  106. // call the correct handlers.
  107.  
  108. LRESULT  (*pfnLBDown)   (HWND, POINT);      // current mouse handler
  109. LRESULT  (*pfnLBUp)     (HWND, POINT);      // functions for rubber
  110. LRESULT  (*pfnMouseMove)(HWND, POINT);      // banding and such
  111.  
  112.  
  113. // The Line, Rectangle, and Ellipse modes use a common set of mouse
  114. // handlers that select 2 points.  The following function pointer is
  115. // used so that the correct drawing function is always used.  Like the
  116. // mouse function pointers, this is set when the drawing mode is
  117. // selected.
  118.  
  119. BOOL (*pfnDrawRect)(HDC, POINT, POINT);     // current drawing function
  120.  
  121.  
  122. // Mouse handling functions for different types of drawing.
  123. LRESULT  PixelLBDown(HWND, POINT);          // Pixels
  124. LRESULT  PixelLBUp(HWND, POINT);
  125. LRESULT  PixelMouseMove(HWND, POINT);
  126.  
  127. LRESULT  BezierLBDown(HWND, POINT);         // Bezier Curves
  128. LRESULT  BezierLBUp(HWND, POINT);
  129. LRESULT  BezierMouseMove(HWND, POINT);
  130.  
  131. LRESULT  RectLBDown(HWND, POINT);           // 2-point selection
  132. LRESULT  RectLBUp(HWND, POINT);             // Used for Lines, Rects,
  133. LRESULT  RectMouseMove(HWND, POINT);        // and Ellipses
  134.  
  135. // Generic drawing functions for use with the above Rect functions
  136. BOOL     LineDraw(HDC, POINT, POINT);
  137. BOOL     RectDraw(HDC, POINT, POINT);
  138. BOOL     EllipseDraw(HDC, POINT, POINT);
  139.  
  140.  
  141. // Other helper functions
  142. VOID StartRubberBand(HWND);
  143. VOID EndRubberBand(HWND hwnd);
  144. VOID SetupScrollBars(HWND, UINT, UINT);
  145.  
  146. // Client window message handling functions
  147. static LRESULT MsgClientCreate      (HWND, UINT, WPARAM, LPARAM);
  148. static LRESULT MsgClientDestroy     (HWND, UINT, WPARAM, LPARAM);
  149. static LRESULT MsgClientMouseMove   (HWND, UINT, WPARAM, LPARAM);
  150. static LRESULT MsgClientLButtonDown (HWND, UINT, WPARAM, LPARAM);
  151. static LRESULT MsgClientLButtonUp   (HWND, UINT, WPARAM, LPARAM);
  152. static LRESULT MsgClientKeyDown     (HWND, UINT, WPARAM, LPARAM);
  153. static LRESULT MsgClientPaint       (HWND, UINT, WPARAM, LPARAM);
  154. static LRESULT MsgClientSize        (HWND, UINT, WPARAM, LPARAM);
  155. static LRESULT MsgClientScroll      (HWND, UINT, WPARAM, LPARAM);
  156.  
  157.  
  158. // Client window message table definitions.
  159. MSD rgmsdClient[] =
  160. {
  161.     {WM_MOUSEMOVE,      MsgClientMouseMove    },
  162.     {WM_LBUTTONDOWN,    MsgClientLButtonDown  },
  163.     {WM_LBUTTONUP,      MsgClientLButtonUp    },
  164.     {WM_KEYDOWN,        MsgClientKeyDown      },
  165.     {WM_CREATE,         MsgClientCreate       },
  166.     {WM_DESTROY,        MsgClientDestroy      },
  167.     {WM_PAINT,          MsgClientPaint        },
  168.     {WM_SIZE,           MsgClientSize         },
  169.     {WM_HSCROLL,        MsgClientScroll       },
  170.     {WM_VSCROLL,        MsgClientScroll       },
  171. };
  172.  
  173.  
  174. MSDI msdiClient =
  175. {
  176.     sizeof(rgmsdClient) / sizeof(MSD),
  177.     rgmsdClient,
  178.     edwpWindow
  179. };
  180.  
  181.  
  182. //
  183. //  FUNCTION: CreateClientWindow(HWND)
  184. //
  185. //  PURPOSE: Create the client window.
  186. //
  187. //  PARAMETERS:
  188. //    hwndParent - The parent (main) window.
  189. //
  190. //  RETURN VALUE:
  191. //    HWND of client window or null if failure.
  192. //
  193. //  COMMENTS:
  194. //
  195.  
  196. HWND CreateClientWindow(HWND hwndParent)
  197. {
  198.     return CreateWindowEx(0,
  199.                           "ClientWndClass",
  200.                           NULL,
  201.                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_HSCROLL | WS_VSCROLL,
  202.                           -100, -100, 10, 10,   // dummy initial coordinates
  203.                           hwndParent,
  204.                           (HMENU)-1,
  205.                           hInst,
  206.                           NULL);
  207. }
  208.  
  209.  
  210. //
  211. //  FUNCTION: ClientWndProc(HWND, UINT, WPARAM, LPARAM)
  212. //
  213. //  PURPOSE:  Processes messages for the client window.
  214. //
  215. //  PARAMETERS:
  216. //    hwnd     - window handle
  217. //    uMessage - message number
  218. //    wparam   - additional information (dependant on message number)
  219. //    lparam   - additional information (dependant on message number)
  220. //
  221. //  RETURN VALUE:
  222. //    The return value depends on the message number.  If the message
  223. //    is implemented in the message dispatch table, the return value is
  224. //    the value returned by the message handling function.  Otherwise,
  225. //    the return value is the value returned by the default window procedure.
  226. //
  227. //  COMMENTS:
  228. //    Call the DispMessage() function with the client window's message
  229. //    dispatch information (msdiClient) and the message specific information.
  230. //
  231.  
  232. LRESULT CALLBACK ClientWndProc(HWND   hwnd,
  233.                                UINT   uMessage,
  234.                                WPARAM wparam,
  235.                                LPARAM lparam)
  236. {
  237.     return DispMessage(&msdiClient, hwnd, uMessage, wparam, lparam);
  238. }
  239.  
  240.  
  241. //
  242. //  FUNCTION: CmdDrawMode(HWND, WORD, WORD, HWND)
  243. //
  244. //  PURPOSE: Handles WM_COMMAND messages to change drawing mode.
  245. //
  246. //  PARAMETERS:
  247. //    hwnd - The main window.
  248. //    wCommand - IDM_LINE, IDM_RECT, etc.
  249. //    wNotify  - Notification number (unused)
  250. //    hwndCtrl - NULL (unused)
  251. //
  252. //  RETURN VALUE:
  253. //    Always returns 0 - command handled.
  254. //
  255. //  COMMENTS:
  256. //
  257.  
  258. #pragma argsused
  259. LRESULT CmdDrawMode(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  260. {
  261.     HMENU hMenu;
  262.  
  263.     if (wCommand == uDrawMode)  // First see if the mode has changed
  264.         return 0;
  265.  
  266.     // Update the menu and toolbar states
  267.  
  268.     hMenu = GetMenu(hwnd);
  269.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_UNCHECKED);
  270.     uDrawMode = wCommand;
  271.     CheckMenuItem(hMenu, uDrawMode, MF_BYCOMMAND | MF_CHECKED);
  272.  
  273.     SendMessage(hWndToolbar, TB_CHECKBUTTON, uDrawMode, MAKELONG(TRUE, 0));
  274.  
  275.     // Set the pointers for the mouse handler functions to
  276.     // the appropriate functions.
  277.  
  278.     switch(uDrawMode)
  279.     {
  280.         case IDM_PIXEL:
  281.             pfnLBDown     = PixelLBDown;
  282.             pfnLBUp       = PixelLBUp;
  283.             pfnMouseMove  = PixelMouseMove;
  284.             break;
  285.  
  286.         case IDM_BEZIER:
  287.             cPoints       = 0;
  288.             pfnLBDown     = BezierLBDown;
  289.             pfnLBUp       = BezierLBUp;
  290.             pfnMouseMove  = BezierMouseMove;
  291.             break;
  292.  
  293.         case IDM_RECT:
  294.             pfnDrawRect   = RectDraw;
  295.             pfnLBDown     = RectLBDown;
  296.             pfnLBUp       = RectLBUp;
  297.             pfnMouseMove  = RectMouseMove;
  298.             break;
  299.  
  300.         case IDM_ELLIPSE:
  301.             pfnDrawRect   = EllipseDraw;
  302.             pfnLBDown     = RectLBDown;
  303.             pfnLBUp       = RectLBUp;
  304.             pfnMouseMove  = RectMouseMove;
  305.             break;
  306.  
  307.         default:
  308.         case IDM_LINE:
  309.             pfnDrawRect   = LineDraw;
  310.             pfnLBDown     = RectLBDown;
  311.             pfnLBUp       = RectLBUp;
  312.             pfnMouseMove  = RectMouseMove;
  313.             break;
  314.     }
  315.  
  316.     return 0;
  317. }
  318.  
  319.  
  320. //
  321. //  FUNCTION: CmdFill(HWND, WORD, WORD, HWND)
  322. //
  323. //  PURPOSE: Handles the IDM_FILL and IDM_NOFILL command messages.
  324. //    Controls whether closed objects (rect's and ellipses) are filled
  325. //    with a brush when drawn.
  326. //
  327. //  PARAMETERS:
  328. //    hwnd     - The main window.
  329. //    wCommand - IDM_FILL or IDM_NOFILL
  330. //    wNotify  - Notification number (unused)
  331. //    hwndCtrl - NULL (unused)
  332. //
  333. //  RETURN VALUE:
  334. //    Always returns 0 - command handled.
  335. //
  336. //  COMMENTS:
  337. //    The toolbar has 2 buttons (IDM_FILL and IDM_NOFILL) but the
  338. //    menu only has IDM_FILL, so this function will turn filling
  339. //    OFF if wCommand is IDM_NOFILL, and TOGGLE the ON/OFF state
  340. //    if wCommand is IDM_FILL.
  341. //
  342.  
  343. #pragma argsused
  344. LRESULT CmdFill(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  345. {
  346.      static BOOL bFill;
  347.  
  348.      if (IDM_NOFILL == wCommand)
  349.         bFill = FALSE;                          // turn filling OFF
  350.     else
  351.         bFill = !bFill;                         // toggle fill state
  352.  
  353.     // Update the menu and toolbar states
  354.  
  355.     CheckMenuItem(GetMenu(hwnd),
  356.                   IDM_FILL,
  357.                   MF_BYCOMMAND | (bFill ? MF_CHECKED : MF_UNCHECKED));
  358.  
  359.     SendMessage(hWndToolbar,
  360.                      TB_CHECKBUTTON,
  361.                 (bFill ? IDM_FILL : IDM_NOFILL),
  362.                 MAKELONG(TRUE, 0));
  363.  
  364.     // Update hbrDraw
  365.     hbrDraw = (bFill ? hbrReal : hbrNull);
  366.     SelectObject(hdcBitmap, hbrDraw);
  367.  
  368.     // If we're recording to a metafile, select the brush
  369.     if (hdcEMF)
  370.         SelectObject(hdcEMF, hbrDraw);
  371.  
  372.      return 0;
  373. }
  374.  
  375.  
  376. //
  377. //  FUNCTION: CmdCreatePen(HWND, WORD, WORD, HWND)
  378. //
  379. //  PURPOSE: Handles the IDM_CREATEPEN command message. Puts up a dialog
  380. //    to let the user set the pen style to use for drawing.
  381. //
  382. //  PARAMETERS:
  383. //    hwnd     - The main window.
  384. //    wCommand - IDM_CREATEPEN (unused)
  385. //    wNotify  - Notification number (unused)
  386. //    hwndCtrl - NULL (unused)
  387. //
  388. //  RETURN VALUE:
  389. //    Always returns 0 - command handled.
  390. //
  391. //  COMMENTS:
  392. //
  393.  
  394. #pragma argsused
  395. LRESULT CmdCreatePen(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  396. {
  397.     LOGPEN lp = logPen;     // copy for the dialog to munge on
  398.  
  399.     if (DialogBoxParam(hInst, "PenDlg", hwnd, (DLGPROC)PenDlg, (LPARAM)&lp))
  400.     {
  401.         HPEN hpenNew = CreatePenIndirect(&lp);      // create new pen
  402.         if (!hpenNew)
  403.         {
  404.             // Should report an error here...
  405.             return 0;
  406.         }
  407.  
  408.         // Select the new pen for drawing (deselect old pen before deleting)
  409.         SelectObject(hdcBitmap, hpenNew);
  410.  
  411.         // If we're recording to a metafile, select the new pen
  412.         if (hdcEMF)
  413.             SelectObject(hdcEMF, hpenNew);
  414.  
  415.         DeleteObject(hpenDraw);                     // delete old pen
  416.         hpenDraw = hpenNew;                         // save changes
  417.         logPen = lp;
  418.     }
  419.  
  420.     return 0;
  421. }
  422.  
  423.  
  424. //
  425. //  FUNCTION: CmdCreateBrush(HWND, WORD, WORD, HWND)
  426. //
  427. //  PURPOSE: Handles the IDM_CREATEBRUSH command message. Puts up a dialog
  428. //    to let the user set the brush style to use for drawing.
  429. //
  430. //  PARAMETERS:
  431. //    hwnd - The main window.
  432. //    wCommand - IDM_CREATEBRUSH (unused)
  433. //    wNotify  - Notification number (unused)
  434. //    hwndCtrl - NULL (unused)
  435. //
  436. //  RETURN VALUE:
  437. //    Always returns 0 - command handled.
  438. //
  439. //  COMMENTS:
  440. //
  441.  
  442. #pragma argsused
  443. LRESULT CmdCreateBrush(HWND hwnd, WORD wCommand, WORD wNotify, HWND hwndCtrl)
  444. {
  445.      LOGBRUSH lb = logBrush; // copy for the dialog to munge on
  446.  
  447.      if (DialogBoxParam(hInst, "BrushDlg", hwnd, (DLGPROC)BrushDlg, (LPARAM)&lb))
  448.     {
  449.         HBRUSH hbrNew = CreateBrushIndirect(&lb);   // create new brush
  450.         if (!hbrNew)
  451.         {
  452.             // Should report an error here...
  453.             return 0;
  454.         }
  455.  
  456.         // If "Fill Objects" is turned on, we need to update hbrDraw
  457.         if (GetMenuState(GetMenu(hwnd), IDM_FILL, MF_BYCOMMAND) & MF_CHECKED)
  458.         {
  459.                 hbrDraw = hbrNew;                       // save new handle
  460.             SelectObject(hdcBitmap, hbrDraw);       // select new brush
  461.                                                     // (deselect old brush)
  462.  
  463.             // If we're recording to a metafile, select the new brush
  464.             if (hdcEMF)
  465.                 SelectObject(hdcEMF, hbrDraw);
  466.         }
  467.  
  468.         DeleteObject(hbrReal);                      // delete old brush
  469.         hbrReal = hbrNew;                           // save changes
  470.         logBrush = lb;
  471.      }
  472.  
  473.     return 0;
  474. }
  475.  
  476.  
  477. //
  478. //  FUNCTION: MsgClientCreate(HWND, UINT, WPARAM, LPARAM)
  479. //
  480. //  PURPOSE: Handles the WM_CREATE message for the client window.
  481. //
  482. //  PARAMETERS:
  483. //
  484. //    hwnd      - Window handle  (Unused)
  485. //    uMessage  - Message number (Unused)
  486. //    wparam    - Extra data     (Unused)
  487. //    lparam    - Extra data     (Unused)
  488. //
  489. //  RETURN VALUE:
  490. //
  491. //  COMMENTS:
  492. //    Initializes various objects.
  493. //
  494.  
  495. #pragma argsused
  496. LRESULT MsgClientCreate(HWND   hwnd,
  497.                                 UINT   uMessage,
  498.                                 WPARAM wparam,
  499.                         LPARAM lparam)
  500. {
  501.     HDC hdcTmp;
  502.     RECT rc;
  503.  
  504.     // Initialize pens and brushes
  505.  
  506.     hpenBlack = GetStockObject(BLACK_PEN);
  507.     hbrNull   = GetStockObject(NULL_BRUSH);
  508.  
  509.     hpenDraw  = hpenBlack;
  510.      hbrReal   = GetStockObject(GRAY_BRUSH);
  511.  
  512.     hbrDraw = hbrReal;
  513.  
  514.     GetObject(hpenDraw, sizeof(logPen), &logPen);
  515.     GetObject(hbrReal, sizeof(logBrush), &logBrush);
  516.  
  517.     // Create off-screen bitmap and DC
  518.  
  519.     hdcTmp = GetDC(hwnd);   // Screen DC for reference
  520.  
  521.     GetClientRect(hwnd, &rc);
  522.  
  523.     cxBitmap = max(rc.right, 1);
  524.     cyBitmap = max(rc.bottom, 1);
  525.     hbmBitmap = CreateCompatibleBitmap(hdcTmp, cxBitmap, cyBitmap);
  526.  
  527.     if (!hbmBitmap)
  528.     {
  529.         DeleteObject(hpenDraw);
  530.         DeleteObject(hbrReal);
  531.  
  532.         OutputDebugString("MsgClientCreate: Unable to create bitmap\r\n");
  533.         return -1;
  534.      }
  535.  
  536.     // Create memory DC to hold bitmap
  537.     hdcBitmap = CreateCompatibleDC(hdcTmp);
  538.     ReleaseDC(hwnd, hdcTmp);  // Done with this
  539.  
  540.     // Initialize memory DC
  541.     SelectObject(hdcBitmap, hpenDraw);
  542.     SelectObject(hdcBitmap, hbrDraw);
  543.     SetBkMode(hdcBitmap, TRANSPARENT);
  544.     hbmStock = SelectObject(hdcBitmap, hbmBitmap);
  545.  
  546.      // Initialize bitmap and create a metafile dc for recording
  547.     ClientNewDrawing();
  548.  
  549.     return 0;
  550. }
  551.  
  552.  
  553. //
  554. //  FUNCTION: MsgClientDestroy(HWND, UINT, WPARAM, LPARAM)
  555. //
  556. //  PURPOSE: Handles WM_DESTROY message for the client window.
  557. //
  558. //  PARAMETERS:
  559. //
  560. //    hwnd      - Window handle  (Unused)
  561. //    uMessage  - Message number (Unused)
  562. //    wparam    - Extra data     (Unused)
  563. //    lparam    - Extra data     (Unused)
  564. //
  565. //  RETURN VALUE:
  566. //
  567. //    Always returns 0 - Message handled
  568. //
  569. //  COMMENTS:
  570. //    General clean up happens here.
  571. //
  572.  
  573. #pragma argsused
  574. LRESULT MsgClientDestroy(HWND   hwnd,
  575.                                  UINT   uMessage,
  576.                                  WPARAM wparam,
  577.                                  LPARAM lparam)
  578. {
  579.     // If we're still recording a metafile, just close and delete it.
  580.     // QuerySaveFile() should prompt the user to save changes prior
  581.     // to getting to this point.
  582.     if (hdcEMF)
  583.     {
  584.         DeleteEnhMetaFile(CloseEnhMetaFile(hdcEMF));
  585.     }
  586.  
  587.     // Reset bitmap DC before deleting objects
  588.  
  589.      SelectObject(hdcBitmap, hpenBlack);
  590.     SelectObject(hdcBitmap, hbrNull);
  591.     SelectObject(hdcBitmap, hbmStock);
  592.  
  593.     if (hPalette)
  594.     {
  595.         SelectPalette(hdcBitmap, GetStockObject(DEFAULT_PALETTE), FALSE);
  596.         DeleteObject(hPalette);
  597.     }
  598.  
  599.     DeleteObject(hpenDraw);   // Delete pens/brushes
  600.     DeleteObject(hbrReal);
  601.  
  602.     DeleteObject(hbmBitmap);  // Delete bitmap and memory DC
  603.     DeleteDC(hdcBitmap);
  604.  
  605.     return 0;
  606. }
  607.  
  608.  
  609. //
  610. //  FUNCTION: MsgClientKeyDown(HWND, UINT, WPARAM, LPARAM)
  611. //
  612. //  PURPOSE: Handles WM_KEYDOWN message.
  613. //
  614. //  PARAMETERS:
  615. //
  616. //    hwnd      - Window handle
  617. //    uMessage  - Message number (Unused)
  618. //    wparam    - Key code (VK_xxx)
  619. //    lparam    - Extra data     (Unused)
  620. //
  621. //  RETURN VALUE:
  622. //
  623. //  COMMENTS:
  624. //    Looks for VK_ESCAPE key only.  If user hits the escape key
  625. //    while drawing, cancel the drawing operation.
  626. //
  627.  
  628. #pragma argsused
  629. LRESULT MsgClientKeyDown(HWND   hwnd,
  630.                          UINT   uMessage,
  631.                          WPARAM wparam,
  632.                          LPARAM lparam)
  633. {
  634.     if (VK_ESCAPE == wparam && bDrawing)
  635.     {
  636.         // End drawing.
  637.         EndRubberBand(hwnd);
  638.         cPoints = 0;
  639.  
  640.           // Force a repaint of the whole window to remove any junk
  641.         // left by the cancelled drawing operation.
  642.         InvalidateRect(hwnd, NULL, TRUE);
  643.     }
  644.  
  645.     return 0;
  646. }
  647.  
  648.  
  649. //
  650. //  FUNCTION: MsgClientPaint(HWND, UINT, WPARAM, LPARAM)
  651. //
  652. //  PURPOSE: Handles WM_PAINT message.
  653. //
  654. //  PARAMETERS:
  655. //
  656. //    hwnd      - Window handle
  657. //    uMessage  - Message number (Unused)
  658. //    wparam    - Extra data     (Unused)
  659. //    lparam    - Extra data     (Unused)
  660. //
  661. //  RETURN VALUE:
  662. //
  663. //  COMMENTS:
  664. //    Calls PaintClientWindow() to do the actual painting.
  665. //
  666.  
  667. #pragma argsused
  668. LRESULT MsgClientPaint(HWND   hwnd,
  669.                        UINT   uMessage,
  670.                        WPARAM wparam,
  671.                        LPARAM lparam)
  672. {
  673.     PAINTSTRUCT ps;
  674.     HPALETTE hpalSave = NULL;   // Handle to previous palette
  675.  
  676.     BeginPaint(hwnd, &ps);
  677.  
  678.     if (hPalette)
  679.     {
  680.           hpalSave = SelectPalette(ps.hdc, hPalette, TRUE);
  681.         RealizePalette(ps.hdc);
  682.     }
  683.  
  684.     BitBlt(ps.hdc,                              // Destination DC
  685.            ps.rcPaint.left,                     // Dest origin
  686.            ps.rcPaint.top,
  687.            ps.rcPaint.right - ps.rcPaint.left,  // Dest extents
  688.            ps.rcPaint.bottom - ps.rcPaint.top,
  689.            hdcBitmap,                           // Source DC
  690.            ps.rcPaint.left + cxHorzScrollPos,   // Source origin
  691.            ps.rcPaint.top  + cyVertScrollPos,
  692.               SRCCOPY);                            // ROP code
  693.  
  694.     if (hPalette)
  695.         SelectPalette(ps.hdc, hpalSave, TRUE);
  696.  
  697.     EndPaint(hwnd, &ps);
  698.     return 0;
  699. }
  700.  
  701.  
  702. //
  703. //  FUNCTION: MsgClientSize(HWND, UINT, WPARAM, LPARAM)
  704. //
  705. //  PURPOSE: Repaints the client window and adjusts the scrollbars
  706. //    when the main window's size has changed.
  707. //
  708. //  PARAMETERS:
  709. //    hwnd - handle of Client window.
  710. //    uMessage - WM_SIZE.
  711. //    wParam - size flag (unused)
  712. //    lParam - width/height of client area (unused)
  713. //
  714. //  RETURN VALUE:
  715. //    0
  716. //
  717. //  COMMENTS:
  718. //    If the window has been enlarged, this will enlarge the bitmap to
  719. //    match the new window size.  The bitmap is never made smaller.
  720. //    SetupScrollBars turns the scrollbars on/off as necessary. This can
  721. //    cause more WM_SIZE messages, so a flag is used to prevent re-entrancy.
  722. //
  723.  
  724. #pragma argsused
  725. LRESULT MsgClientSize(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  726. {
  727.     HDC hdcTmp;
  728.     HBITMAP hbmNew;
  729.     HPALETTE hpalSave;
  730.     UINT cxNew, cyNew;
  731.     UINT cxWindow = LOWORD(lparam);
  732.     UINT cyWindow = HIWORD(lparam);
  733.      static BOOL bInSize = FALSE;
  734.      RECT rc;
  735.  
  736.      // Are we already processing a WM_SIZE message?
  737.     if (bInSize)
  738.         return 0;
  739.  
  740.     bInSize = TRUE;
  741.  
  742.     if (hEMF)
  743.     {        
  744.         // If current thumb positions would cause blank space
  745.         // at right or bottom of client area, repaint
  746.         if ((cxHorzScrollPos + cxWindow > cxPict)
  747.               || (cyVertScrollPos + cyWindow > cyPict))
  748.                 InvalidateRect(hwnd, NULL, TRUE);
  749.     }
  750.  
  751.     // Set up the scroll bars appropriately.
  752.     SetupScrollBars(hwnd, cxPict, cyPict);
  753.  
  754.     // The off-screen bitmap shoule be at least as big as the window
  755.     // and the current metafile, if any.
  756.     cxNew = max(cxPict, cxWindow);
  757.     cyNew = max(cyPict, cyWindow);
  758.  
  759.     // See if we need a bigger bitmap
  760.      if (cxBitmap >= cxNew && cyBitmap >= cyNew)
  761.         goto SizeExit;
  762.  
  763.     // Use a screen DC to create the new bitmap so we get the
  764.     // correct color format.
  765.     hdcTmp = GetDC(hwnd);
  766.     hbmNew = CreateCompatibleBitmap(hdcTmp, cxNew, cyNew);
  767.     ReleaseDC(hwnd, hdcTmp);
  768.  
  769.     // If we couldn't create a new bitmap, just bail out now and leave
  770.     // the old bitmap intact (that's better than having nothing).
  771.     if (hbmNew == NULL)
  772.           goto SizeExit;
  773.  
  774.     // Now we need a memory DC to copy old bitmap to new one.
  775.     hdcTmp = CreateCompatibleDC(NULL);
  776.  
  777.     // Select new bitmap into permanent DC (this deselects the old bitmap)
  778.     // and then select the old bitmap into temporary DC.
  779.     SelectObject(hdcBitmap, hbmNew);
  780.     SelectObject(hdcTmp, hbmBitmap);
  781.  
  782.     if (hPalette)
  783.         hpalSave = SelectPalette(hdcTmp, hPalette, FALSE);
  784.  
  785.     // Initialize the 'extra' parts of the new bitmap with white
  786.  
  787.     if (cxWindow > cxBitmap)
  788.         PatBlt(hdcBitmap, cxBitmap, 0, cxWindow, cyWindow, WHITENESS);
  789.  
  790.     if (cyWindow > cyBitmap)
  791.         PatBlt(hdcBitmap, 0, cyBitmap, cxWindow, cyWindow, WHITENESS);
  792.  
  793.     // Copy old bitmap to new one
  794.     BitBlt(hdcBitmap,
  795.            0, 0,
  796.               cxBitmap, cyBitmap,
  797.            hdcTmp,
  798.            0, 0,
  799.            SRCCOPY);
  800.  
  801.     if (hEMF)
  802.      {
  803.           rc.left = 0;
  804.           rc.top = 0;
  805.           rc.right = cxPict;
  806.           rc.bottom = cyPict;
  807.  
  808.           ExcludeClipRect(hdcBitmap, 0, 0, cxBitmap, cyBitmap);
  809.         PlayEnhMetaFile(hdcBitmap, hEMF, &rc);
  810.         SelectClipRgn(hdcBitmap, NULL);
  811.     }
  812.  
  813.     // Clean up
  814.     if (hPalette)
  815.         SelectPalette(hdcTmp, hpalSave, TRUE);
  816.     SelectObject(hdcTmp, hbmStock);
  817.     DeleteObject(hbmBitmap);
  818.     DeleteDC(hdcTmp);
  819.  
  820.      // Save new info
  821.     hbmBitmap = hbmNew;
  822.     cxBitmap = cxNew;
  823.     cyBitmap = cyNew;
  824.  
  825. SizeExit:
  826.     bInSize = FALSE;
  827.     return 0;
  828. }
  829.  
  830.  
  831. //
  832. //  FUNCTION: MsgClientScroll(HWND, UINT, WPARAM, LPARAM)
  833. //
  834. //  PURPOSE: Scrolls the client window in response to scroll message.
  835. //
  836. //  PARAMETERS:
  837. //    hwnd - handle of Client window. 
  838. //    uMessage - the scroll message.
  839. //    wParam - contains scroll position and scrollbar type
  840. //    lParam - (unused)
  841. //
  842. //  RETURN VALUE:
  843. //    None
  844. //
  845. //  COMMENTS:
  846. //    MsgClientScroll performs scrolling in both horiziontal and vertical directions.
  847. //    If the user clicks on one of the scrolling arrows, the window is scrolled
  848. //    by (1 / SCROLL_RATIO) of the client area.  For example, if 
  849. //    SCROLL_RATIO == 4, then the client area is moved over a 1/4 of the 
  850. //    width or height (as the case may be) of the screen.  If the user pages
  851. //    up/down the window is scrolled an amount equal to the client area's
  852. //    width or height as the case may be. If the user moves the thumb to an 
  853. //    absolute position, the window is scrolled accordingly.
  854. //
  855. //
  856.  
  857. #define SCROLL_RATIO 8
  858.  
  859. #pragma argsused
  860. LRESULT MsgClientScroll(HWND hwnd, UINT uMessage, WPARAM wparam, LPARAM lparam)
  861. {
  862.     int nScrollCode;    // SB_HORZ if doing horizontal, SB_VERT if doing vertical
  863.     int nPos;           // Scrollbar position.
  864.     int nLine;          // # of pixels for LINEUP/LINEDOWN
  865.     int nMove;          // How much to move.
  866.      SCROLLINFO si;      // Current scroll info: nMin, nMax, nPage, nPos
  867.  
  868.     nScrollCode = (uMessage == WM_HSCROLL ? SB_HORZ : SB_VERT);
  869.  
  870.     // Get current scroll info
  871.     si.cbSize = sizeof(si);
  872.     si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
  873.  
  874.     GetScrollInfo(hwnd, nScrollCode, &si);
  875.  
  876.     // One page is always visible, so the true range that we allow scrolling over
  877.     // is actually min to (max - page size), so adjust for that here.
  878.      si.nMax -= si.nPage;
  879.  
  880.     nPos = si.nPos;     // Save the current position
  881.  
  882.     // On a SB_LINEUP/SB_LINEDOWN we will move the picture by
  883.     //  1 / SCROLL_RATIO of the page size (i.e. if SCROLL_RATIO
  884.     //  is 4, it will scroll one quarter of the window)
  885.  
  886.     nLine = si.nPage / SCROLL_RATIO;
  887.     if (nLine == 0)
  888.         nLine = 1;
  889.  
  890.      switch (GET_WM_HSCROLL_CODE(wparam, lparam))
  891.     {
  892.         case SB_LINEDOWN:             // One line right/down.
  893.             nMove = nLine;
  894.             break;
  895.  
  896.         case SB_LINEUP:               // One line left/up.
  897.             nMove = -nLine;
  898.             break;
  899.  
  900.         case SB_PAGEDOWN:             // One page right/down.
  901.             nMove = si.nPage;
  902.                 break;
  903.  
  904.         case SB_PAGEUP:               // One page left/up.
  905.             nMove = -(int)si.nPage;
  906.             break;
  907.  
  908.         case SB_THUMBPOSITION:        // Absolute position.
  909.             nMove = GET_WM_HSCROLL_POS(wparam, lparam) - nPos;
  910.             break;
  911.  
  912.         default:                      // No change.
  913.             nMove = 0;
  914.                 break;
  915.     }
  916.  
  917.     if (nMove)
  918.     {
  919.         nPos += nMove;              // Calculate new scroll pos
  920.  
  921.         if (nPos < si.nMin)         // Make sure min <= pos <= max
  922.         {
  923.             nMove -= nPos - si.nMin;
  924.             nPos = si.nMin;
  925.         }
  926.  
  927.         if (nPos > si.nMax)
  928.         {
  929.             nMove -= nPos - si.nMax;
  930.             nPos = si.nMax;
  931.         }
  932.  
  933.         if (nMove)                  // Update the window's scroll pos and redraw
  934.         {
  935.             SetScrollPos(hwnd, nScrollCode, nPos, TRUE);
  936.  
  937.             // Set the global scroll pos variable
  938.                 if (nScrollCode == SB_HORZ)
  939.                 cxHorzScrollPos = nPos;
  940.             else 
  941.                 cyVertScrollPos = nPos;
  942.  
  943.             RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
  944.         }
  945.     }
  946.     return 0;
  947. }
  948.  
  949.  
  950. //
  951. //  FUNCTION: MsgClientLButtonDown(HWND, UINT, WPARAM, LPARAM)
  952. //
  953. //  PURPOSE: Handles WM_LBUTTONDOWN message.
  954. //
  955. //  PARAMETERS:
  956. //
  957. //    hwnd      - Window handle
  958. //    uMessage  - Message number (Unused)
  959. //    wparam    - Extra data     (Unused)
  960. //    lparam    - Mouse coordinates
  961. //
  962. //  RETURN VALUE:
  963. //
  964. //  COMMENTS:
  965. //    Initiates dragging/drawing operation by saving the mouse
  966. //    position and capturing mouse input.
  967. //
  968.  
  969. #pragma argsused
  970. LRESULT MsgClientLButtonDown(HWND   hwnd,
  971.                                       UINT   uMessage,
  972.                              WPARAM wparam,
  973.                              LPARAM lparam)
  974. {
  975.     POINT pt;
  976.  
  977.     // If we aren't already drawing, set focus to the client window
  978.     // so we can get Escape key messages should the user decide to
  979.     // quit drawing.
  980.  
  981.     if (!bDrawing)
  982.         SetFocus(hwnd);
  983.  
  984.     // Get mouse position
  985.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  986.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  987.  
  988.     // Call the current LBDown handler
  989.     return (*pfnLBDown)(hwnd, pt);
  990. }
  991.  
  992.  
  993. //
  994. //  FUNCTION: MsgClientMouseMove(HWND, UINT, WPARAM, LPARAM)
  995. //
  996. //  PURPOSE: Handles WM_MOUSEMOVE message.
  997. //
  998. //  PARAMETERS:
  999. //
  1000. //    hwnd      - Window handle
  1001. //    uMessage  - Message number (Unused)
  1002. //    wparam    - Extra data     (Unused)
  1003. //    lparam    - Mouse coordinates
  1004. //
  1005. //  RETURN VALUE:
  1006. //
  1007. //  COMMENTS:
  1008. //    Performs "rubber-banding" by erasing the previous image and
  1009. //    redrawing the object using an XOR ROP code.
  1010. //
  1011.  
  1012. #pragma argsused
  1013. LRESULT MsgClientMouseMove(HWND   hwnd,
  1014.                            UINT   uMessage,
  1015.                            WPARAM wparam,
  1016.                            LPARAM lparam)
  1017. {
  1018.     POINT pt;
  1019.     static char szBuf[20] ;     // Array for formatting mouse coordinates
  1020.  
  1021.     // Get new mouse position
  1022.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  1023.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  1024.  
  1025.     // Update the status bar with the new position
  1026.     wsprintf(szBuf, "%d,%d", pt.x, pt.y);
  1027.     UpdateStatusBar(szBuf, 2, 0);
  1028.  
  1029.     // Call the current MouseMove handler
  1030.     return (*pfnMouseMove)(hwnd, pt);
  1031. }
  1032.  
  1033.  
  1034. //
  1035. //  FUNCTION: MsgClientLButtonUp(HWND, UINT, WPARAM, LPARAM)
  1036. //
  1037. //  PURPOSE: Handles WM_LBUTTONUP message.
  1038. //
  1039. //  PARAMETERS:
  1040. //
  1041. //    hwnd      - Window handle
  1042. //    uMessage  - Message number (Unused)
  1043. //    wparam    - Extra data     (Unused)
  1044. //    lparam    - Mouse coordinates
  1045. //
  1046. //  RETURN VALUE:
  1047. //
  1048. //  COMMENTS:
  1049. //    Erases previous rubber-band image and draws the object in
  1050. //      the final position.
  1051. //
  1052.  
  1053. #pragma argsused
  1054. LRESULT MsgClientLButtonUp(HWND   hwnd,
  1055.                                     UINT   uMessage,
  1056.                            WPARAM wparam,
  1057.                            LPARAM lparam)
  1058. {
  1059.     POINT pt;
  1060.  
  1061.     // Get new mouse position
  1062.     pt.x = (int)(short)LOWORD(lparam) + cxHorzScrollPos;
  1063.     pt.y = (int)(short)HIWORD(lparam) + cyVertScrollPos;
  1064.  
  1065.     // Call the current LBUp handler
  1066.     return (*pfnLBUp)(hwnd, pt);
  1067. }
  1068.  
  1069.  
  1070. //
  1071. //  FUNCTION: xxxLBDown(HWND, POINT)
  1072. //  FUNCTION: xxxMouseMove(HWND, POINT)
  1073. //  FUNCTION: xxxLBUp(HWND, POINT)
  1074. //
  1075. //  PURPOSE: Handles WM_LBUTTONDOWN, WM_MOUSEMOVE, and WM_LBUTTONUP
  1076. //
  1077. //  PARAMETERS:
  1078. //
  1079. //    hwnd - Window handle
  1080. //    pt   - Mouse coordinates
  1081. //
  1082. //  RETURN VALUE:
  1083. //    Always return 0
  1084. //
  1085. //  COMMENTS:
  1086. //    These functions are called by the actual Msgxxx mouse message
  1087. //    handler functions (above) to perform input processing.  Three
  1088. //    sets of these functions are implemented here.  One to input
  1089. //    individual pixels; one to select 2 points (used to draw a
  1090. //    line, rectangle, or ellipse); and one to select 4 points (for
  1091. //    drawing Bezier curves).  Additional functions could be "plugged
  1092. //    in" to support other types of drawing.
  1093. //
  1094. //    In general, xxxLBDown initializes the drawing process, for example
  1095. //    by saving the initial coordinates and calling StartRubberBand;
  1096. //    xxxMouseMove updates the coordinates; and xxxLBUp performs any
  1097. //    final processing and clean up (EndRubberBand).
  1098. //
  1099.  
  1100. //
  1101. //  FUNCTION: PixelLBDown(HWND, POINT)
  1102. //  FUNCTION: PixelMouseMove(HWND, POINT)
  1103. //  FUNCTION: PixelLBUp(HWND, POINT)
  1104. //
  1105. //  PURPOSE: Perform single point selection (individual pixels).
  1106. //
  1107.  
  1108. LRESULT PixelLBDown(HWND hwnd, POINT pt)
  1109. {
  1110.     // Get a DC and capture mouse input
  1111.     StartRubberBand(hwnd);
  1112.     SetROP2(hdcRB, R2_COPYPEN);
  1113.  
  1114.     // Draw the pixel with the current pen color into both the
  1115.     // rubber-banding DC and the bitmap DC.
  1116.     SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  1117.     SetPixelV(hdcBitmap, pt.x, pt.y, logPen.lopnColor);
  1118.  
  1119.     // If we're recording to a metafile, draw there as well
  1120.     if (hdcEMF)
  1121.         SetPixelV(hdcEMF, pt.x, pt.y, logPen.lopnColor);
  1122.  
  1123.     return 0;
  1124. }
  1125.  
  1126.  
  1127. #pragma argsused
  1128. LRESULT PixelMouseMove(HWND hwnd, POINT pt)
  1129. {
  1130.     // If we are currently drawing, draw the pixel
  1131.     if (bDrawing)
  1132.     {
  1133.         SetPixelV(hdcRB, pt.x, pt.y, logPen.lopnColor);
  1134.         SetPixelV(hdcBitmap, pt.x, pt.y, logPen.lopnColor);
  1135.  
  1136.         // If we're recording to a metafile, draw there as well
  1137.         if (hdcEMF)
  1138.             SetPixelV(hdcEMF, pt.x, pt.y, logPen.lopnColor);
  1139.     }
  1140.  
  1141.     return 0;
  1142. }
  1143.  
  1144.  
  1145. #pragma argsused
  1146. LRESULT PixelLBUp(HWND hwnd, POINT pt)
  1147. {
  1148.     // Don't draw the pixel again here (it's already drawn at LBDown
  1149.     // and MouseMove time).  Just need to clean up.
  1150.  
  1151.     if (bDrawing)
  1152.     {
  1153.         bModify = TRUE;
  1154.         EndRubberBand(hwnd);
  1155.     }
  1156.  
  1157.     return 0;
  1158. }
  1159.  
  1160.  
  1161. //
  1162. //  FUNCTION: BezierLBDown(HWND, POINT)
  1163. //  FUNCTION: BezierMouseMove(HWND, POINT)
  1164. //  FUNCTION: BezierLBUp(HWND, POINT)
  1165. //
  1166. //  PURPOSE: Perform 4-point selection for drawing Bezier curves.
  1167. //
  1168.  
  1169. #pragma argsused
  1170. LRESULT BezierLBDown(HWND hwnd, POINT pt)
  1171. {
  1172.     // If this is the first click, initialize rubber banding.
  1173.  
  1174.     if (0 == cPoints)
  1175.         StartRubberBand(hwnd);
  1176.  
  1177.      return 0;
  1178. }
  1179.  
  1180.  
  1181. #pragma argsused
  1182. LRESULT BezierMouseMove(HWND hwnd, POINT pt)
  1183. {
  1184.     int i;
  1185.  
  1186.     if (!bDrawing || 0 == cPoints)              // Are we currently drawing?
  1187.         return 0;
  1188.  
  1189.     // Are we rubber banding a Bezier?
  1190.     if (cPoints >= 2)
  1191.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS); // Erase previous Bezier
  1192.  
  1193.     // Erase previous line segment
  1194.      LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1195.  
  1196.     // Set the rest of the points to be the same as this one
  1197.     for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1198.         pPoints[i] = pt;
  1199.  
  1200.     // Draw new line segment
  1201.     LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1202.  
  1203.     // Are we rubber banding a Bezier?
  1204.     if (cPoints >= 2)
  1205.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1206.  
  1207.     return 0;
  1208. }
  1209.  
  1210.  
  1211. LRESULT BezierLBUp(HWND hwnd, POINT pt)
  1212. {
  1213.     int i;
  1214.  
  1215.     if (!bDrawing)          // Are we currently drawing?
  1216.         return 0;
  1217.  
  1218.     if (cPoints >= 2)       // Are we rubber banding a Bezier?
  1219.     {
  1220.         // Erase previous Bezier
  1221.         PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1222.  
  1223.         if (cPoints == BEZ_MAXPOINTS - 1) // Is this the last point?
  1224.         {
  1225.             // If so, then erase all line segments.
  1226.             for (i = BEZ_MAXPOINTS - 1; i > 0; i--)
  1227.                 LineDraw(hdcRB, pPoints[i - 1], pPoints[i]);
  1228.         }
  1229.     }
  1230.     // else do *not* erase the previous line segments
  1231.  
  1232.     // Save new position and increment # of points
  1233.     pPoints[cPoints++] = pt;
  1234.  
  1235.     if (cPoints < BEZ_MAXPOINTS)    // Still more points to get.
  1236.     {
  1237.         // Set the rest of the points to be the same as the current one
  1238.         for (i = cPoints; i < BEZ_MAXPOINTS; i++)
  1239.             pPoints[i] = pt;
  1240.  
  1241.         // Draw new line segment
  1242.         LineDraw(hdcRB, pPoints[cPoints - 1], pPoints[cPoints]);
  1243.  
  1244.         // Once we have at least 2 points let's draw the Bezier curve
  1245.         if (cPoints >= 2)
  1246.             PolyBezier(hdcRB, pPoints, BEZ_MAXPOINTS);
  1247.     }
  1248.     else    // Finish this Bezier and quit drawing.
  1249.     {
  1250.         // Setup DC for 'real' drawing
  1251.         SetROP2(hdcRB, R2_COPYPEN);
  1252.         SetBkMode(hdcRB, TRANSPARENT);
  1253.         SelectObject(hdcRB, hpenDraw);
  1254.  
  1255.         // Draw Bezier in permanent position
  1256.         PolyBezier(hdcRB, pPoints, cPoints);
  1257.  
  1258.         // Draw it again into the bitmap DC
  1259.         PolyBezier(hdcBitmap, pPoints, cPoints);
  1260.  
  1261.         // If we're recording to a metafile, draw there as well
  1262.         if (hdcEMF)
  1263.             PolyBezier(hdcEMF, pPoints, cPoints);
  1264.  
  1265.         // De-select the pen and clean up rubber banding stuff.
  1266.         SelectObject(hdcRB, hpenBlack);
  1267.         EndRubberBand(hwnd);
  1268.         cPoints = 0;
  1269.         bModify = TRUE;
  1270.     }
  1271.  
  1272.     return 0;
  1273. }
  1274.  
  1275.  
  1276. //
  1277. //  FUNCTION: RectLBDown(HWND, UINT, WPARAM, LPARAM)
  1278. //  FUNCTION: RectMouseMove(HWND, UINT, WPARAM, LPARAM)
  1279. //  FUNCTION: RectLBUp(HWND, UINT, WPARAM, LPARAM)
  1280. //
  1281. //  PURPOSE: Perform 2-point selection for drawing lines, rectangles,
  1282. //    and ellipses.
  1283. //
  1284.  
  1285. LRESULT RectLBDown(HWND hwnd, POINT pt)
  1286. {
  1287.     // Current position = starting position = mouse position
  1288.     pPoints[0] = pPoints[1] = pt;
  1289.  
  1290.     // Initialize rubber banding
  1291.     StartRubberBand(hwnd);
  1292.  
  1293.     // Draw initial state
  1294.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1295.  
  1296.     return 0;
  1297. }
  1298.  
  1299.  
  1300. #pragma argsused
  1301. LRESULT RectMouseMove(HWND hwnd, POINT pt)
  1302. {
  1303.     if (!bDrawing)          // Are we currently drawing?
  1304.         return 0;
  1305.  
  1306.     // Un-draw previous position by redrawing
  1307.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1308.  
  1309.     // Save new position
  1310.     pPoints[1] = pt;
  1311.  
  1312.     // Draw in new position
  1313.      (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1314.  
  1315.     return 0;
  1316. }
  1317.  
  1318.  
  1319. LRESULT RectLBUp(HWND hwnd, POINT pt)
  1320. {
  1321.     if (!bDrawing)          // Are we currently drawing?
  1322.         return 0;
  1323.  
  1324.     // Un-draw previous position by redrawing
  1325.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1326.  
  1327.     // Save new position
  1328.     pPoints[1] = pt;
  1329.  
  1330.     // Setup DC for 'real' drawing
  1331.     SetROP2(hdcRB, R2_COPYPEN);
  1332.     SetBkMode(hdcRB, TRANSPARENT);
  1333.     SelectObject(hdcRB, hpenDraw);
  1334.     SelectObject(hdcRB, hbrDraw);
  1335.  
  1336.     // Draw object in permanent position
  1337.     (*pfnDrawRect)(hdcRB, pPoints[0], pPoints[1]);
  1338.  
  1339.     // Draw it again into the bitmap DC
  1340.     (*pfnDrawRect)(hdcBitmap, pPoints[0], pPoints[1]);
  1341.  
  1342.     // If we're recording to a metafile, draw there as well
  1343.     if (hdcEMF)
  1344.         (*pfnDrawRect)(hdcEMF, pPoints[0], pPoints[1]);
  1345.  
  1346.     // De-select the pen and brush by selecting stock objects
  1347.     // and clean up rubber banding stuff.
  1348.     SelectObject(hdcRB, hpenBlack);
  1349.     SelectObject(hdcRB, hbrNull);
  1350.     EndRubberBand(hwnd);
  1351.     bModify = TRUE;
  1352.  
  1353.     return 0;
  1354. }
  1355.  
  1356.  
  1357. //
  1358. //  FUNCTION: xxxxDraw(HDC hdc, POINT pt1, POINT pt2)
  1359. //
  1360. //  PURPOSE: Draws a Line, Rectangle, or Ellipse at the coordinates
  1361. //    given by pt1 and pt2.
  1362. //
  1363. //  PARAMETERS:
  1364. //    hdc   - Device context to draw into.
  1365. //    pt1   - Beginning point (upper-left corner of rect)
  1366. //    pt2   - Ending point    (lower-right corner of rect)
  1367. //
  1368. //  RETURN VALUE:
  1369. //    TRUE for success, FALSE otherwise.
  1370. //
  1371. //  COMMENTS:
  1372. //    Assumes the DC is already correctly set up.
  1373. //
  1374. //
  1375.  
  1376. BOOL LineDraw(HDC hdc, POINT pt1, POINT pt2)
  1377. {
  1378.     MoveToEx(hdc, pt1.x, pt1.y, NULL);
  1379.     return LineTo(hdc, pt2.x, pt2.y);
  1380. }
  1381.  
  1382.  
  1383. BOOL RectDraw(HDC hdc, POINT pt1, POINT pt2)
  1384. {
  1385.     return Rectangle(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1386. }
  1387.  
  1388.  
  1389. BOOL EllipseDraw(HDC hdc, POINT pt1, POINT pt2)
  1390. {
  1391.     return Ellipse(hdc, pt1.x, pt1.y, pt2.x, pt2.y);
  1392. }
  1393.  
  1394.  
  1395. //
  1396. //  FUNCTION: StartRubberBand(HWND)
  1397. //
  1398. //  PURPOSE: Sets up DC for rubber banding and captures the mouse.
  1399. //
  1400. //  PARAMETERS:
  1401. //    hwnd - Window for GetDC
  1402. //
  1403. //  RETURN VALUE:
  1404. //    None
  1405. //
  1406. //  COMMENTS:
  1407. //
  1408.  
  1409. VOID StartRubberBand(HWND hwnd)
  1410. {
  1411.     if (bDrawing)   // If we're already drawing, just return.
  1412.         return;
  1413.  
  1414.     hdcRB = GetDC(hwnd);                        // Get a DC to draw to
  1415.  
  1416.     // adjust the viewport origin for scrolled view
  1417.     SetViewportOrgEx(hdcRB, -cxHorzScrollPos, -cyVertScrollPos, NULL); 
  1418.  
  1419.     // Select our logical palette so palette-relative colors work correctly
  1420.     if (hPalette)
  1421.         SelectPalette(hdcRB, hPalette, TRUE);
  1422.  
  1423.     // R2_NOT causes drawing with a pen to invert the screen pixels.
  1424.     // This makes it easy to erase something by drawing it a 2nd time.
  1425.     SetROP2(hdcRB, R2_NOT);
  1426.  
  1427.     // For rubber banding with R2_NOT, we use a single pixel pen and
  1428.     // a NULL brush.
  1429.     SelectObject(hdcRB, hpenBlack);
  1430.     SelectObject(hdcRB, hbrNull);
  1431.  
  1432.     SetCapture(hwnd);                           // Capture mouse input
  1433.     bDrawing = TRUE;
  1434. }
  1435.  
  1436.  
  1437. //
  1438. //  FUNCTION: EndRubberBand(HWND, BOOL)
  1439. //
  1440. //  PURPOSE: Releases rubber banding DC and mouse capture.
  1441. //
  1442. //  PARAMETERS:
  1443. //    hwnd - Window for ReleaseDC
  1444. //    bSave - TRUE ==> save edits; FALSE ==> don't save
  1445. //
  1446. //  RETURN VALUE:
  1447. //    None
  1448. //
  1449. //  COMMENTS:
  1450. //
  1451.  
  1452. VOID EndRubberBand(HWND hwnd)
  1453. {
  1454.     // clean up the rubber-band DC
  1455.     if (hPalette)
  1456.         SelectPalette(hdcRB, GetStockObject(DEFAULT_PALETTE), TRUE);
  1457.  
  1458.     ReleaseDC(hwnd, hdcRB);
  1459.  
  1460.     // turn off drawing
  1461.     ReleaseCapture();
  1462.     bDrawing = FALSE;
  1463. }
  1464.  
  1465.  
  1466. //
  1467. //  FUNCTION: ClientNewDrawing(VOID)
  1468. //
  1469. //  PURPOSE: Reset everything... we've either just created a new
  1470. //    document or opened an existing one.
  1471. //
  1472. //  PARAMETERS:
  1473. //    None
  1474. //
  1475. //  RETURN VALUE:
  1476. //    None
  1477. //
  1478. //  COMMENTS:
  1479. //
  1480.  
  1481. VOID ClientNewDrawing(VOID)
  1482. {
  1483.     RECT rc;
  1484.     UINT cxNew, cyNew;
  1485.     HBITMAP hbmNew;
  1486.     HPALETTE hPal = NULL;
  1487.     HCURSOR hcurSave = SetCursor(hcursHourGlass);
  1488.  
  1489.     // Assume the user has already been asked to save changes
  1490.     if (hdcEMF)
  1491.     {
  1492.         DeleteEnhMetaFile(CloseEnhMetaFile(hdcEMF));
  1493.         hdcEMF = NULL;
  1494.     }
  1495.  
  1496.     if (hWndClient)
  1497.         GetClientRect(hWndClient, &rc);
  1498.     else
  1499.     {
  1500.         rc.right = rc.bottom = 0;
  1501.     }
  1502.  
  1503.     if (hEMF)
  1504.     {
  1505.         cxPict = PictureWidth(hEMF);
  1506.         cyPict = PictureHeight(hEMF);
  1507.         hPal   = CreateMetaPalette(hEMF);
  1508.     }
  1509.     else
  1510.     {
  1511.         cxPict = 0;
  1512.         cyPict = 0;
  1513.     }
  1514.  
  1515.     // Use a default palette if necessary
  1516.     if (bPalDevice && !hPal)
  1517.     {
  1518.         HDC hdc;
  1519.  
  1520.         hdc = GetDC(NULL);
  1521.         hPal = CreateHalftonePalette(hdc);
  1522.         ReleaseDC(NULL, hdc);
  1523.     }
  1524.  
  1525.     // Calculate necessary bitmap dimensions
  1526.     cxNew = max(cxPict, (UINT)rc.right);
  1527.     cyNew = max(cyPict, (UINT)rc.bottom);
  1528.  
  1529.     // Create a new bitmap if the current one isn't big enough
  1530.     if (cxNew > cxBitmap || cyNew > cyBitmap)
  1531.     {
  1532.         HDC hdcTemp = GetDC(NULL);
  1533.  
  1534.         hbmNew = CreateCompatibleBitmap(hdcTemp, cxNew, cyNew);
  1535.         ReleaseDC(NULL, hdcTemp);
  1536.  
  1537.         if (hbmNew)
  1538.         {
  1539.             // de-select and delete old bitmap
  1540.             SelectObject(hdcBitmap, hbmNew);
  1541.             DeleteObject(hbmBitmap);
  1542.  
  1543.             // save new bitmap info
  1544.             hbmBitmap = hbmNew;
  1545.             cxBitmap = cxNew;
  1546.             cyBitmap = cyNew;
  1547.         }
  1548.     }
  1549.  
  1550.     // Do we have a palette?
  1551.     if (hPal)
  1552.     {
  1553.         // Select the new palette (de-select the old one)
  1554.         SelectPalette(hdcBitmap, hPal, FALSE);
  1555.  
  1556.         if (hPalette)                       // Delete old palette
  1557.             DeleteObject(hPalette);
  1558.  
  1559.         hPalette = hPal;                    // Save new palette
  1560.  
  1561.         // Force foreground realization
  1562.         if (hWndClient)
  1563.             PostMessage(GetParent(hWndClient), WM_QUERYNEWPALETTE, 0, 0);
  1564.     }
  1565.  
  1566.     // Erase bitmap with white    
  1567.     PatBlt(hdcBitmap, 0, 0, cxBitmap, cyBitmap, WHITENESS);
  1568.  
  1569.     // Initialize bitmap
  1570.     if (hEMF)
  1571.     {
  1572.         SetRect(&rc, 0, 0, cxPict, cyPict);
  1573.         PlayEnhMetaFile(hdcBitmap, hEMF, &rc);
  1574.     }
  1575.  
  1576.     // Get a metafile DC to record changes to
  1577.     hdcEMF = GetDCFromEMF(hWndClient, hEMF, hPalette);
  1578.  
  1579.     // Initialize metafile DC
  1580.     SelectObject(hdcEMF, hpenDraw);
  1581.     SelectObject(hdcEMF, hbrDraw);
  1582.     SetBkMode(hdcEMF, TRANSPARENT);
  1583.  
  1584.     if (hWndClient)
  1585.     {
  1586.         // Turn scrollbars on/off
  1587.         SetupScrollBars(hWndClient, cxPict, cyPict);
  1588.  
  1589.         // Update the screen
  1590.         InvalidateRect(hWndClient, NULL, TRUE);
  1591.     }
  1592.  
  1593.     // Restore cursor
  1594.     SetCursor(hcurSave);
  1595. }
  1596.  
  1597.  
  1598. //
  1599. //  FUNCTION: SetScrollBar(HWND, int, UINT, UINT)
  1600. //
  1601. //  PURPOSE: Calculate scroll bar range and thumb size depending on the
  1602. //    size of the window and the size of the picture to display.
  1603. //
  1604. //  PARAMETERS:
  1605. //    hwnd     - handle of client window.
  1606. //    nCode    - either SB_HORZ or SB_VERT
  1607. //    cxWindow - dimension of client area (horz or vert)
  1608. //    cxPict   - dimension of picture (horz or vert)
  1609. //
  1610. //  RETURN VALUE:
  1611. //    The new scroll position.
  1612. //
  1613. //  COMMENTS:
  1614. //    Works for either horizontal or vertical scroll bars.  If no scroll
  1615. //    bar is necessary, the scroll bar is disabled rather than hidden.
  1616. //
  1617.  
  1618. int SetScrollBar(HWND hWnd, int nCode, UINT cxWindow, UINT cxPict)
  1619. {
  1620.     SCROLLINFO si;
  1621.  
  1622.     si.cbSize = sizeof(si);
  1623.     si.fMask  = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL;
  1624.     si.nMin   = 0;
  1625.     si.nMax   = cxPict;
  1626.     si.nPage  = cxWindow;
  1627.     si.nPos   = 0;
  1628.  
  1629.     // Update the scrollbar and return new scroll position
  1630.     return SetScrollInfo(hWnd, nCode, &si, TRUE /* fRepaint */);
  1631. }
  1632.  
  1633.  
  1634. //
  1635. //  FUNCTION: SetupScrollBars(HWND, UINT, UINT)
  1636. //
  1637. //  PURPOSE: Adjust scroll bar ranges and thumb sizes depending on the size
  1638. //              of the window and the picture to display.
  1639. //
  1640. //  PARAMETERS:
  1641. //    hwnd - handle of client window.
  1642. //    cxPict - width of picture.
  1643. //    cyPict - height of picture.
  1644. //
  1645. //  RETURN VALUE:
  1646. //    none.
  1647. //
  1648. //  COMMENTS:
  1649. //    Calls SetScrollBar for each scroll bar to do the work
  1650. //
  1651.  
  1652. VOID SetupScrollBars(HWND hWnd, UINT cxPict, UINT cyPict)
  1653. {
  1654.     RECT       rcClient;                // Client area rectangle
  1655.  
  1656.     GetClientRect(hWnd, &rcClient);
  1657.  
  1658.     cxHorzScrollPos = SetScrollBar(hWnd, SB_HORZ, rcClient.right,  cxPict);
  1659.     cyVertScrollPos = SetScrollBar(hWnd, SB_VERT, rcClient.bottom, cyPict);
  1660. }
  1661.  
  1662.  
  1663. //
  1664. //  FUNCTION: GetCurrentEMF(VOID)
  1665. //
  1666. //  PURPOSE: Return a handle to a metafile containing the current
  1667. //    modifications.
  1668. //
  1669. //  PARAMETERS:
  1670. //    None
  1671. //
  1672. //  RETURN VALUE:
  1673. //    Returns a handle to an enhanced metafile or NULL
  1674. //
  1675. //  COMMENTS:
  1676. //
  1677.  
  1678. HENHMETAFILE GetCurrentEMF(VOID)
  1679. {
  1680.     HENHMETAFILE hemf;
  1681.  
  1682.     // Have we made any modifications?
  1683.     if (!bModify)
  1684.         return hEMF;
  1685.  
  1686.     if (!hdcEMF)
  1687.         return NULL;
  1688.  
  1689.     // Get metafile with current modifications from the metafile DC
  1690.     hemf = CloseEnhMetaFile(hdcEMF);
  1691.  
  1692.     // Create a new metafile DC and initialize it
  1693.     hdcEMF = GetDCFromEMF(hWndClient, hemf, hPalette);
  1694.  
  1695.     SelectObject(hdcEMF, hpenDraw);
  1696.     SelectObject(hdcEMF, hbrDraw);
  1697.     SetBkMode(hdcEMF, TRANSPARENT);
  1698.  
  1699.     return hemf;
  1700. }
  1701.